#ifndef HMS_SETTINGS_HPP
#define HMS_SETTINGS_HPP

#include "logger.hpp"
#include "meshGen.hpp"
#include "sweSolver.hpp"
#include "rasterFormats.hpp"
#include "simulationTime.hpp"
#include "invalidByDefault.hpp"

#include "INIReader_extension.hpp"

#include <array>
#include <filesystem>
#include <optional>
#include <string>


namespace hms
{
namespace fs = std::filesystem;

constexpr auto settingsDir    { "settings" };
constexpr auto settingsFile   { "hms.ini" };


struct ImportSettings : public InvalidByDefault {
	using RasterFormat = hms::raster::Format;

	fs::path importPath;
	bool
		generateMesh     {false},
		optimiseMeshType {false};

	enum class Flag {
		never,
		always,
		ifNotFound,
		ifNewer
	};

	enum class MeshFormat {
		gmsh
	};

	template<typename Format>
	struct ImportItem {
		Flag import;
		bool wasImported {false};
		Format format;
		fs::path file;
		fs::path formatFile;
		bool interpolate {false}; // only used for time series
	};

	ImportItem<MeshFormat> mesh;
	ImportItem<RasterFormat> dem;
	ImportItem<RasterFormat> roughness;
	ImportItem<RasterFormat> rain;
};


struct ExportSettings : public InvalidByDefault {
	bool exportResults {true};

	enum class Format {
		vtk
	};

	Format format;
};


struct ProbeSettings {
	bool enabled {false};
	std::string fileName;
	std::string colSep {" "};
	Array2Xs coords;
};


struct BenchmarkSettings : public InvalidByDefault {
	bool exportBenchmarkResults {false};

	enum class BenchCase {
		lateralFlow,
		diagonalFlow,
		dambreak_dry,
		dambreak_wet,
	};
	BenchCase benchCase;

};


enum class WhichSolver {
	swe
};

/**
 * @brief Aggregate class to hold settings and to make passing settings to
 * functions easier.
 * Changed from POD struct to class to implement checks in accessor functions.
 * 
 */
class Settings {
public:
	static constexpr auto dir  { "settings" };
	static constexpr auto file { "hms.ini"  };

	enum class Path {
		caseDir,
		bindingFile,
		vtkFile,
		boundaryFile,
		constFields,
		sourceFields,
		nPaths
	};

	bool
		printVersion     {false},
		quiet            {false},
		dryRun           {false},
		clearFiles       {false},
		runBenchmarkCase {false},
		resumeSimulation {false},
		dummyVtkFile     {false},
		dummyFileCreated {false};

	std::optional<log::Level> logLevel {std::nullopt};

	ini::INIReader iniReader;

	enum class ImportItem {
		all,
		mesh,
		dem,
		roughness,
		rainfall
	};

	std::optional<ImportItem> importItem {std::nullopt};

private:
	mutable std::shared_ptr<log::logger> m_logger {nullptr};
	std::array<fs::path, static_cast<int>(Path::nPaths)> m_paths;

	std::string m_caseName;

	std::vector<ProbeSettings> m_probes;
	bool m_probesExist {false};

	MeshGenSettings   m_meshGen;
	TimeSettings      m_time;
	SWESettings       m_swe;
	BenchmarkSettings m_benchmark;
	ImportSettings    m_import;
	ExportSettings    m_exportSettings;


public:
	void setLogger();
	auto logger() const -> log::logger&; // logger is mutable

	auto path( Path name ) const -> const fs::path&;
	void path( Path name, fs::path&& p );

	auto caseDir         () const -> const fs::path&;
	auto vtkFilePath     () const -> const fs::path&;
	auto bindingFilePath () const -> const fs::path&;
	auto boundaryPath    () const -> const fs::path&;
	auto constFieldsPath () const -> const fs::path&;
	auto sourceFieldsPath() const -> const fs::path&;

	auto vtkFilePath() -> fs::path&;

	auto caseName() const -> const std::string&;
	void caseName( std::string&& );


	#define SETTINGS_ACCESSORS(TYPE, NAME) \
	void NAME( TYPE&& ); \
	auto NAME() const -> const TYPE&; \
	auto NAME() -> TYPE&;

	SETTINGS_ACCESSORS(MeshGenSettings, meshGen)
	SETTINGS_ACCESSORS(TimeSettings, time)
	SETTINGS_ACCESSORS(SWESettings, swe)
	SETTINGS_ACCESSORS(BenchmarkSettings, benchmark)
	SETTINGS_ACCESSORS(ImportSettings, import)
	SETTINGS_ACCESSORS(ExportSettings, exportSettings)

	#undef SETTINGS_ACCESSORS

	void probes( std::vector<ProbeSettings>&& ps );
	std::vector<ProbeSettings>& probes();
};

} // namespace hms


#endif